package SC12Project.Milestone1.Warehouse;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * Service provider for a Warehouse.
 * 
 * @author Diego Havenstein
 * 
 */
public class WarehouseProvider implements WarehouseWS {

	private String fn = "../datasource/ds_2_2.xml";
	private Unmarshaller u;
	private WareHouse wl;

	/**
	 * Constructor
	 * 
	 * @throws FileNotFoundException
	 */
	public WarehouseProvider() throws FileNotFoundException {
		parseAndInit(fn);
	}

	void parseAndInit(String path) throws FileNotFoundException {

		try {
			ClassLoader cl = SC12Project.Milestone1.Warehouse.ObjectFactory.class.getClassLoader();
			String packageName = WareHouse.class.getPackage().getName();
			JAXBContext jc = JAXBContext.newInstance(packageName, cl);
			this.u = jc.createUnmarshaller();
			@SuppressWarnings("unchecked")
			JAXBElement<WareHouse> root = (JAXBElement<WareHouse>) u.unmarshal(new FileInputStream(fn));
			wl = root.getValue();

		} catch (JAXBException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Query the amount of available items in the warehouse by resourceID
	 * 
	 * @param resourceID
	 *            the resourceID of the item in the warehouse
	 * @return the amount of the item in the warehouse
	 * 
	 * 
	 */
	@Override
	public int query(String resourceID) {
		int n = wl.getItems().getItem().size();
		for (int i = 0; i < n; i++)
			if (wl.getItems().getItem().get(i).getResourceID().equals(resourceID))
				return wl.getItems().getItem().get(i).getAmount();
		return 0;
	}

	/**
	 * Remove the amount of items from the warehouse
	 * 
	 * @param resourceID
	 *            the resourceID of the item in the warehouse
	 * @param amount
	 *            the amount of items need to be picked up
	 * @return true if the operation is successfull; otherwise false
	 * @exception NotEnoughItemException
	 * 
	 * 
	 */
	@Override
	public boolean pickupItems(String resourceID, int amount) throws NotEnoughItemException {
		int n = wl.getItems().getItem().size();
		for (int i = 0; i < n; i++)
			if (wl.getItems().getItem().get(i).getResourceID().equals(resourceID))
				if (amount <= wl.getItems().getItem().get(i).getAmount()) {
					try {
						wl.getItems().getItem().get(i).setAmount(wl.getItems().getItem().get(i).getAmount() - amount);
						marshalIntoXMLFile();
					} catch (FileNotFoundException e) {
						e.printStackTrace();
					} catch (JAXBException e) {
						e.printStackTrace();
					}
					return true;
				} else
					throw new NotEnoughItemException();
		return false;
	}

	/**
	 * Complement the amount of items to the warehouse
	 * 
	 * @param resourceID
	 *            the resourceID of the item in the warehouse
	 * @param amount
	 *            the amount of items need to complement
	 * @return the new amount of items in the warehouse
	 * 
	 * 
	 */
	@Override
	public int complementStock(String resourceID, int amount) {
		int n = wl.getItems().getItem().size();
		for (int i = 0; i < n; i++)
			if (wl.getItems().getItem().get(i).getResourceID().equals(resourceID)) {
				wl.getItems().getItem().get(i).setAmount(wl.getItems().getItem().get(i).getAmount() + amount);
				try {
					marshalIntoXMLFile();
				} catch (FileNotFoundException e) {

					e.printStackTrace();
				} catch (JAXBException e) {

					e.printStackTrace();
				}
				return wl.getItems().getItem().get(i).getAmount();
			}
		// couldn't find item with this resourceID. Create new entry in items list with initial amount amount.
		ItemInfo ii = new ItemInfo();
		ii.setAmount(amount);
		ii.setResourceID(resourceID);
		wl.getItems().getItem().add(ii);

		try {
			marshalIntoXMLFile();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (JAXBException e) {
			e.printStackTrace();
		}

		return amount;
	}

	/**
	 * Hold some items for later use. The holding items are invisible to the query operation. The items can only be accessed by the ID of the holding request.
	 * 
	 * @param resourceID
	 *            the resourceID of the item in the warehouse
	 * @param amount
	 *            the amount of items need to be held
	 * @return the ID of the holding request (the ID can be used to pickup items or cancel the holding)
	 * 
	 * 
	 */
	@Override
	public String holdItems(String resourceID, int amount) throws NotEnoughItemException {
		int index = -1;
		for (int i = 0; i < wl.getItems().getItem().size(); i++)
			if (wl.getItems().getItem().get(i).getResourceID().equals(resourceID))
				index = i;
		if (index == -1 || amount > wl.getItems().getItem().get(index).getAmount())
			throw new NotEnoughItemException();
		else {
			Double id = getGaussian(100, 10);
			String holdingID = id.toString() + "-" + (new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss")).format(new Date()).toString();
			pickupItems(resourceID, amount);
			HoldingRequestInfo hri = new HoldingRequestInfo();
			ItemInfo ii = new ItemInfo();
			ii.setAmount(amount);
			ii.setResourceID(resourceID);
			hri.setItem(ii);
			hri.setRequestID(holdingID);
			wl.holdingReques.getRequest().add(hri);
			try {
				marshalIntoXMLFile();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (JAXBException e) {
				e.printStackTrace();
			}
			return holdingID;
		}
	}

	/**
	 * Cancel the holding request. The holding items are released.
	 * 
	 * @param holdingID
	 *            the ID of the holding request
	 * 
	 * 
	 */
	@Override
	public void cancelHoldingItems(String holdingID) {
		boolean cancelled = false;
		for (int i = 0; i < wl.getHoldingReques().getRequest().size() && !cancelled; i++)
			if (wl.getHoldingReques().getRequest().get(i).getRequestID().equals(holdingID)) {
				// return items to warehouse
				complementStock(wl.getHoldingReques().getRequest().get(i).getItem().getResourceID(), wl.getHoldingReques().getRequest().get(i).getItem().getAmount());
				// delete holding request
				wl.getHoldingReques().getRequest().remove(i);
				try {
					marshalIntoXMLFile();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (JAXBException e) {
					e.printStackTrace();
				}
				cancelled = true;
			}
		// would love to throw InvalidHoldingIDException here, but it is not allowed in provided interface ...
	}

	/**
	 * Pick up the holding items. Remove holding items from the warehouse
	 * 
	 * @param holdingID
	 *            the ID of the holding request
	 * @return true if the operation is successful; otherwise false
	 * @exception the
	 *                holdingID is invalid
	 * 
	 * 
	 */
	@Override
	public boolean pickupHoldingItems(String holdingID) throws InvalidHoldingIDException {
		for (int i = 0; i < wl.getHoldingReques().getRequest().size(); i++)
			if (wl.getHoldingReques().getRequest().get(i).getRequestID().equals(holdingID)) {
				wl.getHoldingReques().getRequest().remove(i);
				try {
					marshalIntoXMLFile();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (JAXBException e) {
					e.printStackTrace();
				}
				return true;
			}
		throw new InvalidHoldingIDException();
	}

	static double getGaussian(double mean, double variance) {
		return mean + (new Random()).nextGaussian() * variance;
	}

	/**
	 * Method marshals wl object into f
	 * 
	 * @throws JAXBException
	 * @throws FileNotFoundException
	 */
	void marshalIntoXMLFile() throws JAXBException, FileNotFoundException {
		ClassLoader cl = SC12Project.Milestone1.Warehouse.ObjectFactory.class.getClassLoader();
		String packageName = WareHouse.class.getPackage().getName();
		JAXBContext jc = JAXBContext.newInstance(packageName, cl);
		Marshaller m = jc.createMarshaller();
		m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
		ObjectFactory obf = new ObjectFactory();
		JAXBElement<WareHouse> output = obf.createWarehouse(wl);
		m.marshal(output, new FileOutputStream(fn));
	}

}
